package com.yugao.fintech.framework.validation.config;

import com.yugao.fintech.framework.core.config.ValidatorProperties;
import com.yugao.fintech.framework.validation.EnumValue;
import org.apache.commons.lang3.StringUtils;
import org.hibernate.validator.HibernateValidator;
import org.hibernate.validator.messageinterpolation.ResourceBundleMessageInterpolator;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.validation.Validator;
import org.springframework.validation.beanvalidation.LocalValidatorFactoryBean;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;

import javax.annotation.PostConstruct;
import javax.annotation.Resource;
import javax.validation.Validation;
import javax.validation.ValidatorFactory;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.Locale;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

@Configuration
public class ValidationConfig implements WebMvcConfigurer {
    private static final Logger log = LoggerFactory.getLogger(ValidationConfig.class);
    private static ValidatorProperties validatorProper;

    @Resource
    private ValidatorProperties validatorProperties;

    @PostConstruct
    public void init() {
        validatorProper = validatorProperties;
    }

    private static final Map<String, String> VALIDATION_ANNATATION_DEFAULT_MESSAGES =
            new HashMap<String, String>(20) {{
                // put("Min", "validation.message.min");
                // put("NotNull", "validation.message.notNull");
                put("xxx", "validation.message.notNull");
            }};

    /**
     * 存放枚举值，用于提示用户消息
     */
    private static final Map<String, String> enumMessage = new ConcurrentHashMap<String, String>(32) {{
        put("EnumValue", "");
    }};

    @Override
    public Validator getValidator() {
        LocalValidatorFactoryBean localValidatorFactoryBean = new LocalValidatorFactoryBean();
        localValidatorFactoryBean.setMessageInterpolator(new MessageInterpolator());
        return localValidatorFactoryBean;
    }

    private static class MessageInterpolator extends ResourceBundleMessageInterpolator {
        private static final String ENUM_VALUE = "EnumValue";

        @Override
        public String interpolate(String message, Context context, Locale locale) {

            // 获取注解类型
            String annotationTypeName = context.getConstraintDescriptor().getAnnotation().annotationType().getSimpleName();

            if (validatorProper.getIsAddEnumValue() && ENUM_VALUE.equals(annotationTypeName)) {
                EnumValue enumValue = (EnumValue) context.getConstraintDescriptor().getAnnotation();
                Class<? extends Enum<?>> aClass = enumValue.enumClass();
                String name = aClass.getName();
                String result = enumMessage.get(name);
                if (StringUtils.isEmpty(result)) {
                    try {
                        // 错误的方式，枚举对应的class没有newInstance方法，会报NoSuchMethodException，应该使用getEnumConstants方法
                        //Object o = aClass.newInstance();NoSuchMethodException
                        // Object[] oo = aClass.getEnumConstants();
                        // result = (String) method.invoke(oo[0]);
                        result = getEnumMessage(aClass);
                        enumMessage.put(name, result);
                    } catch (Exception e) {
                        log.error(e.getMessage());
                        e.printStackTrace();
                    }
                }
                message = message + " [" + result + "]";
            }

            // 根据注解类型获取自定义的消息Code
            String annotationDefaultMessageCode = VALIDATION_ANNATATION_DEFAULT_MESSAGES.get(annotationTypeName);
            if (null != annotationDefaultMessageCode && !message.startsWith("javax.validation")
                    && !message.startsWith("org.hibernate.validator.constraints")) {
                // 如果注解上指定的message不是默认的javax.validation或者org.hibernate.validator等开头的情况，
                // 则需要将自定义的消息Code拼装到原message的后面；
                message += "{" + annotationDefaultMessageCode + "}";
            }

            return super.interpolate(message, context, locale);
        }
    }

    /**
     * 通过反射调用枚举类的values获取所有枚举类
     *
     * @param enumClass 枚举类
     * @return 枚举类
     */
    @SuppressWarnings("unchecked")
    private static <E> E[] values(Class<E> enumClass)
            throws NoSuchMethodException, InvocationTargetException, IllegalAccessException {
        Method valuesMethod = enumClass.getMethod("values");
        Object valuesObj = valuesMethod.invoke(enumClass);
        return (E[]) valuesObj;
    }

    /**
     * 获取枚举信息
     *
     * @param aClass 枚举类
     */
    public static String getEnumMessage(Class<? extends Enum<?>> aClass) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException {
        Enum<?>[] values = values(aClass);
        StringBuilder sb = new StringBuilder();
        for (Enum<?> value : values) {
            Method getDesc = value.getClass().getMethod("getDesc");
            String desc = (String) getDesc.invoke(value);

            Method getCode = value.getClass().getMethod("getCode");
            Object code = getCode.invoke(value);
            sb.append(code);
            if (!StringUtils.isEmpty(desc)) {
                sb.append("(").append(desc).append(")");
            }
            sb.append(",");
        }
        sb.replace(sb.lastIndexOf(","), sb.length(), "");
        return sb.toString();
    }

    /**
     * 配置hibernate Validator为快速失败返回模式：
     *
     * @return Validator
     */
    @Bean
    public javax.validation.Validator validator() {
        ValidatorFactory validatorFactory = Validation.byProvider(HibernateValidator.class)
                .configure()
                .failFast(true)
                .buildValidatorFactory();
        return validatorFactory.getValidator();
    }
}
